package com.c2soft.optimized;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.c2soft.ncmxSocket.QuoteData;

public class SocketClient {

	private String hostname;
	private int port;

	Socket socketClient;
	private OutputStream out;

	public SocketClient(final String hostname, final int port) {
		this.hostname = hostname;
		this.port = port;
	}

	public void connect() throws UnknownHostException, IOException {
		System.out.println("Attempting to connect to " + hostname + ":" + port);
		socketClient = new Socket(hostname, port);
		System.out.println("Connection Established");
	}

	public void listen() throws UnknownHostException, IOException {

		connect();
		InputStream in = socketClient.getInputStream();

		writeToOutputStream(createLoginRequest());

		while (true) {
			int totalBytesRcvd = 0; // Total bytes received so far

			int bytesRcvd = 0; // Bytes received in last read

			byte[] responseBuffer = new byte[5000];

			if ((bytesRcvd = in.read(responseBuffer, totalBytesRcvd,
					responseBuffer.length)) == -1) {

				throw new SocketException("Connection closed prematurely");
			} else {
				if (bytesRcvd > 0) {
					short val = ByteBuffer.wrap(responseBuffer)
							.order(java.nio.ByteOrder.LITTLE_ENDIAN).getShort();
					switch (val) {
					case 1051:
						processLogin(responseBuffer);
						break;
					case 50:
						processForDataPackets(responseBuffer);
						break;
					default:
						break;
					}

				}
			}

		}

	}

	private void processForDataPackets(byte[] responseBuffer) {
		short noofPkt = ByteBuffer.wrap(responseBuffer, 2, 2)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN).getShort();
		short exchID = ByteBuffer.wrap(responseBuffer, 4, 2)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN).getShort();

		int constant = noofPkt * 82;
		constant += 100;
		byte[] result = new byte[constant];
		ByteBuffer.wrap(responseBuffer, 4, result.length).get(result);
		int index = 0;
		for (int pktInd = 0; pktInd <= noofPkt; pktInd++) {
			ExecutorService executorSvc= Executors.newFixedThreadPool(12);
			executorSvc.submit(new ReadPacketCallable(result));
		}

	}

	private void processLogin(byte[] loginResByte) {
		int userID = ByteBuffer.wrap(loginResByte, 2, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN).getInt();
		short oLoginStat = ByteBuffer.wrap(loginResByte, 6, 2)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN).getShort();
		// String exchs = ByteBuffer.wrap(loginResByte, 8,
		// 25).toString();
		byte[] oServIP = new byte[16];
		byte[] oftpUserName = new byte[20];
		byte[] oftpPwd = new byte[20];
		ByteBuffer.wrap(loginResByte, 8, 16).get(oServIP);
		String serverIP = new String(oServIP);

		ByteBuffer.wrap(loginResByte, 24, 44).get(oftpUserName);
		String ftpUserName = new String(oftpUserName);

		ByteBuffer.wrap(loginResByte, 24, 44).get(oftpPwd);
		String ftpPwd = new String(oftpPwd);
		System.out.println("serverIP=" + serverIP + " \n ftpUserName="
				+ ftpUserName + "\n ftpPwd=" + ftpPwd);

	}

	private void writeToOutputStream(byte[] req) {
		try {
			OutputStream out = socketClient.getOutputStream();
			out.write(req);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private byte[] createLoginRequest() {
		Socket socket = null;
		Short txnCode = 1001;

		String userName = padString("demo", 25);
		String password = padString("demo@123", 25);
		String apiCode = padString("SAT", 20);

		byte[] loginReqByte1 = new byte[2];
		loginReqByte1[0] = (byte) (txnCode & 0xff);
		loginReqByte1[1] = (byte) ((txnCode >> 8) & 0xff);

		loginReqByte1 = concat(loginReqByte1, userName.getBytes(),
				password.getBytes(), apiCode.getBytes());

		return loginReqByte1;
	}

	private void readLoginResponse() {
		// TODO Auto-generated method stub

	}

	private void readQuotePacket() {
		// TODO Auto-generated method stub

	}

	public void close() {
		try {
			socketClient.close();
			System.out.println("Connection closed succefully");
		} catch (Exception e) {

		} finally {
			try {
				socketClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

	private static String padString(String str, int leng) {
		StringBuilder sb = new StringBuilder(str);
		for (int i = str.length(); i <= leng; i++)
			sb.append(" ");
		return sb.toString();
	}

	private static byte[] concat(byte[]... arrays) {
		int lengh = 0;
		for (byte[] array : arrays) {
			lengh += array.length;
		}
		byte[] result = new byte[lengh];
		int pos = 0;
		for (byte[] array : arrays) {
			for (byte element : array) {
				result[pos] = element;
				pos++;
			}
		}
		return result;
	}
}

class ReadPacketCallable implements Callable{
	
	private  byte[] result;
	private Map tokenIdSymbolMap;

	public ReadPacketCallable(final byte[] result,Map tokenIdSymbolMap) {
		this.result=result;
		this.tokenIdSymbolMap=tokenIdSymbolMap;
	}

	public String call() throws Exception {
		byte[] qutPkt = new byte[86];

		try {
			ByteBuffer.wrap(result, index,
					qutPkt.length).get(qutPkt);
		} catch (Exception e) {
			break;
		}

		short exchID_ = ByteBuffer
				.wrap(qutPkt, 0, 2)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getShort();
		int tokenID = ByteBuffer
				.wrap(qutPkt, 2, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		String symbol=tokenIdSymbolMap.get(String.valueOf(tokenID) );
		
		
		int bid1 = ByteBuffer
				.wrap(qutPkt, 6, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		int bidQ1 = ByteBuffer
				.wrap(qutPkt, 10, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		int ask1 = ByteBuffer
				.wrap(qutPkt, 14, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		int askQ1 = ByteBuffer
				.wrap(qutPkt, 18, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();

		int ltp = ByteBuffer
				.wrap(qutPkt, 22, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		int ltq = ByteBuffer
				.wrap(qutPkt, 26, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		int ltt = ByteBuffer
				.wrap(qutPkt, 30, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		int open = ByteBuffer
				.wrap(qutPkt, 34, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		int high = ByteBuffer
				.wrap(qutPkt, 38, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		int low = ByteBuffer
				.wrap(qutPkt, 42, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		int close = ByteBuffer
				.wrap(qutPkt, 46, 4)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getInt();
		double tbq = ByteBuffer
				.wrap(qutPkt, 50, 8)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getDouble();
		double tsq = ByteBuffer
				.wrap(qutPkt, 58, 8)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getDouble();
		double vol = ByteBuffer
				.wrap(qutPkt, 66, 8)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getDouble();
		double openInt = ByteBuffer
				.wrap(qutPkt, 74, 8)
				.order(java.nio.ByteOrder.LITTLE_ENDIAN)
				.getDouble();
		
		QuoteData quoteData = null;
		if(symbol !=null){
		 quoteData = new QuoteData(exchID_,
				tokenID,symbol, bid1, bidQ1, ask1, askQ1, ltp,
				ltq, ltt, open, high, low, close, tbq,
				tsq, vol, openInt);
		 System.out.println(quoteData);
		return "Success";
	}
	
}